package com.taco.springcloud.generate.service;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.converts.MySqlTypeConvert;
import com.baomidou.mybatisplus.generator.config.po.TableFill;
import com.baomidou.mybatisplus.generator.config.rules.DbColumnType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.taco.springcloud.generate.dao.GenerateMapper;
import com.taco.springcloud.generate.model.dto.GenerateDTO;
import com.taco.springcloud.generate.model.dto.ColumnDTO;
import com.taco.springcloud.generate.model.dto.PageDTO;
import com.taco.springcloud.generate.model.dto.TableDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;


/**
 *  代码生成器
 *
 *  @author Yu
 *  @date 2019/10/3 16:14
 */

@Slf4j
@Service
public class GenerateService {

    @Resource
    private GenerateMapper generateMapper;

    /**
     * 分页查找表
     *
     * @param schema    名称空间
     * @param tableName 表名称
     * @param pageDTO   分页参数
     * @return          表元数据
     */
    public IPage<TableDTO> selectAll(String schema, String tableName, PageDTO pageDTO) {
        return generateMapper.selectTables(pageDTO.buildQueryPage(), schema, tableName);
    }

    /**
     * 通过名称空间和表名称查找表元数据
     *
     * @param schema    名称空间
     * @param tableName 表名称
     * @return          表元数据
     */
    public TableDTO selectTable(String schema, String tableName) {
        return generateMapper.selectTableBySchemaAndTableName(schema, tableName);
    }

    /**
     * 通过名称空间和表名称查找表列元数据
     *
     * @param schema    名称空间
     * @param tableName 表名称
     * @return          列元数据
     */
    public List<ColumnDTO> queryColumns(String schema, String tableName) {
        return generateMapper.selectColumnsBySchemaAndTableName(schema, tableName);
    }

    /**
     * 生成代码
     *
     * @param generateDTO 生成配置
     */
    public void execute(GenerateDTO generateDTO) {

        AutoGenerator autoGenerator = new AutoGenerator();

        GlobalConfig globalConfig = getGlobalConfig(generateDTO);
        autoGenerator.setGlobalConfig(globalConfig);

        DataSourceConfig dataSourceConfig = getDataSourceConfig(generateDTO);
        autoGenerator.setDataSource(dataSourceConfig);

        StrategyConfig strategy = getStrategyConfig(generateDTO);
        autoGenerator.setStrategy(strategy);

        PackageConfig packConfig = getPackConfig(generateDTO);
        autoGenerator.setPackageInfo(packConfig);

        InjectionConfig injectionConfig = getInjectionConfig(globalConfig, packConfig, generateDTO);
        autoGenerator.setCfg(injectionConfig);

        autoGenerator.execute();
    }

    /**
     * 设置自定义模板配置
     *
     * @param globalConfig      全局配置类
     * @param packageConfig     包路径配置类
     * @return                  自定义模板配置类
     */
    private InjectionConfig getInjectionConfig(GlobalConfig globalConfig, PackageConfig packageConfig, GenerateDTO generateDTO) {
        // 自定义配置
        InjectionConfig cfg = new InjectionConfig() {
            @Override
            public void initMap() {
                Map<String, Object> params = Maps.newHashMapWithExpectedSize(2);
                params.put("datetime", DateUtil.format(new Date(), "yyyy/M/dd HH:mm"));
                params.put("schema", generateDTO.getSchema());
                this.setMap(params);
            }
        };
        return cfg;
    }

    /**
     * 设置系统默认模板的路径
     *
     * @param generateDTO   代码生成器配置DTO
     * @return              代码生成器包路径配置类
     */
    private TemplateConfig getTemplateConfig(GenerateDTO generateDTO) {
        TemplateConfig templateConfig = new TemplateConfig();
        //设置默认Entity模板路径
        //templateConfig.setEntity(ConstVal.TEMPLATE_ENTITY_JAVA);
        return templateConfig;
    }

    /**
     * 获取全局配置类
     *
     * @param generateDTO   代码生成器配置DTO
     * @return              代码生成器全局配置类
     */
    private GlobalConfig getGlobalConfig(GenerateDTO generateDTO) {
        GlobalConfig gc = new GlobalConfig();
        gc.setOutputDir(generateDTO.getOutputDir());
        gc.setFileOverride(true);
        gc.setBaseResultMap(true);
        gc.setBaseColumnList(true);
        gc.setEnableCache(false);
        gc.setOpen(false);
        gc.setAuthor(generateDTO.getAuthor());
        gc.setSwagger2(true);
        gc.setIdType(IdType.valueOf(generateDTO.getIdType()));
        // 自定义文件命名，注意 %s 会自动填充表实体属性！
        gc.setControllerName("%sController");
        gc.setServiceName("%sService");
        gc.setServiceImplName("%sService");
        gc.setMapperName("%sDao");
        gc.setXmlName("%sMapper");
        return gc;
    }

    /**
     * 获取数据源配置
     *
     * @param generateDTO   代码生成器配置DTO
     * @return              代码生成器数据源配置类
     */
    private DataSourceConfig getDataSourceConfig(GenerateDTO generateDTO) {
        DataSourceConfig dsc = BeanUtil.toBean(generateDTO, DataSourceConfig.class);


        dsc.setTypeConvert(new MySqlTypeConvert() {
            // 自定义数据库表字段类型转换【可选】
            @Override
            public DbColumnType processTypeConvert(GlobalConfig globalConfig, String fieldType) {
                //将数据库中datetime转换成date
                String s = fieldType.toLowerCase();
                if (s.contains("datetime")) {
                    return DbColumnType.DATE;
                }
                //将数据库中tinyint转换成integer
                if(s.contains("tinyint")) {
                    return DbColumnType.INTEGER;
                }
                return (DbColumnType) super.processTypeConvert(globalConfig, fieldType);
            }
        });

        return dsc;
    }

    /**
     * 获取策略配置类
     *
     * @param generateDTO   代码生成器配置DTO
     * @return              代码生成器策略配置类
     */
    private StrategyConfig getStrategyConfig(GenerateDTO generateDTO) {

        StrategyConfig strategy = new StrategyConfig();
        strategy.setRestControllerStyle(true);
        strategy.setEntityLombokModel(true);
        strategy.setEntityTableFieldAnnotationEnable(false);
        // 此处可以移除表前缀表前缀
        strategy.setTablePrefix(generateDTO.getTablePrefix());

        // 表名生成策略
        strategy.setNaming(NamingStrategy.underline_to_camel);
        // 字段生成策略
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);

        // 需要生成的表
        strategy.setInclude(generateDTO.getIncludeTables());
        strategy.setVersionFieldName("opt");
        strategy.setLogicDeleteFieldName("is_del");

        // 公共字段填充
        ArrayList<TableFill> tableFills = Lists.newArrayListWithCapacity(2);
        tableFills.add(new TableFill("createTime", FieldFill.INSERT));
        tableFills.add(new TableFill("updateTime", FieldFill.UPDATE));
        strategy.setTableFillList(tableFills);
        return strategy;
    }

    /**
     * 获取包配置类
     *
     * @param generateDTO   代码生成器配置DTO
     * @return              代码生成器包配置类
     */
    private PackageConfig getPackConfig(GenerateDTO generateDTO) {
        PackageConfig pc = new PackageConfig();
        //父包名
        pc.setParent(generateDTO.getParentPackage());
        //父包模块名
        pc.setModuleName(generateDTO.getModuleName());
        //实体类父包
        pc.setEntity(generateDTO.getModelPackage());
        //controller父包
        pc.setController(generateDTO.getControllerPackage());
        //mapper父包
        pc.setMapper(generateDTO.getDaoPackage());
        //xml父包
        pc.setXml(generateDTO.getMapperPackage());
        pc.setServiceImpl(generateDTO.getServicePackage());
        pc.setService(generateDTO.getServicePackage());
        return pc;
    }
}
